/*
\begin{listingcont}
*/
/**
**			     **
** Prozess mit 4 UserThreads **
**			     **
**/
// Include threads
#include "threads.h"
#include "ringpuffer.h"

///Ringpuffer von Blatt2
//
//RB12 fuer Thread 1 und Thread 2
//RB23 fuer Thread 2 und Thread 3
//RB34 fuer Thread 3 und Thread 4
struct Rb_handle_t *RB12, *RB23, *RB34;

int main(int argc, char *argv[]) 
{
	
	int skSize = 4096;

	// Prioritaet von jeder Thread
	int p1, p2, p3, p4;

	if(argc == 1)
	{
		p1 = p2 = p3 = p4 = 0;
		printf("\tAlle Threads haben die gleiche Prioritaet = 0\n\n");
	}
	
	else if(argc == 2)
	{
		p1 = p2 = p3 = p4 = atoi(argv[1]);
		printf("\tAlle Threads haben die gleiche Prioritaet = %d\n\n", p1);
	}
	else if(argc == 5)
	{
		p1 = atoi(argv[1]);
		p2 = atoi(argv[2]);
		p3 = atoi(argv[3]);
		p4 = atoi(argv[4]);
		printf("\tp1 = %d, p2 = %d, p3 = %d, p4 = %d\n\n", p1, p2, p3, p4);
	}
	else
	{
		printf("\t Fehler in Parameter\n \tAufruf:\n");
		printf("\t threads : alle Threads haben die Prioritaet 0\n");
		printf("\t threads p: p alle threads haben die gleiche Prioritaet p\n");
		printf("\t threads p1 p2 p3 p4: p1 ... p4 Prioritaet von Thread p1 ... p4\n\n");
		exit(0);
	}

	// Speicher reservieren
	RB12 = initRb( 1, 2, 400);
	RB23 = initRb( 2, 3, 400);
	RB34 = initRb( 3, 4, 400);
	
	// Signal Handler initialisieren	
	signal(SIGTERM, signalhandler);
	signal(SIGINT, signalhandler);

	/// Threads registrieren
	schedRegisterUserThread(thread1, "T1", skSize, "T1", p1);
	schedRegisterUserThread(thread2, "T2", skSize, "T2", p2);
	schedRegisterUserThread(thread3, "T3", skSize, "T3", p3);
	schedRegisterUserThread(thread4, "T4", skSize, "T4", p4);
	
	// Scheduler Starten
	schedActivateUserThread();
	
	// Speicher freiegeben
	freeRb(RB12);
	freeRb(RB23);
	freeRb(RB34);
	
	return 0;
}
/**
* Schreiben in einem Ringpuffer
*
* Solange kein Speicherplatz da ist oder kein Fehler auftritt, 
* wird es versucht zu schreiben
*/
int schreib(struct Rb_handle_t *hdl, int i, int j, char* s, int size)
{
	int retval = 0; //
	do{	
		retval = writeRb(hdl, i, j, s, size); // Eine Tentative
		if(retval == -1 && (size <= RB12->freieplatz || size > RB12->buffersize)) //Stop wenn Fehler
			return 0;
		//Wenn nicht geschaft und kein Fehler, dann CPU abgeben
		else if(retval == -1)
			schedYield();
	}while(retval<0);

	return 1;
}

/**
* Lesen aus einem Ringpuffer
*
* Solange keine Daten vorhanden ist oder kein Fehler auftritt, wird
* weiter versuch auszulesen.
*/
int lies(struct Rb_handle_t *hdl, int i, int j, char* s, size_t* size)
{
	int retval = 0;
	int initSize = *size;
	do{	
		*size = initSize;
		// Eine Tentative
		retval = readRb(hdl, i, j, s, size);
		// Stop wenn Fehler
		if(retval < 0)
			return 0;
		// Sonst cpu abgabe, wenn Puffer leer war
		else if(retval == 0)
			schedYield();
	}while(retval == 0);
	return 1;
}

/**
* Erste Thread
*
* Schreib Eingabe der Console in RB12
*/
int thread1(void* args)
{
           
	struct timeval tv;
	int retval = 0;
	fd_set rfds;
	while(1)
	{
           	// Eine Sekunde warten
         	tv.tv_sec = 1;
		tv.tv_usec = 0;

 		FD_ZERO(&rfds);
		FD_SET(0, &rfds);
	
		retval = select(1, &rfds, NULL, NULL, &tv);
		
		// Wenn Fehler oder keine Daten
		// dann CPU freigeben
		if (retval == -1 || !retval)
	         {
			if(retval == -1) 
		      		perror("select()");
			schedYield();
			continue;
		 }
		
		retval = 0;
		char input[40];
		// Console einlesen
		gets( input);
		// In ringpuffer speichern (RB12)
		if(!schreib(RB12, 1, 2, input, strlen(input)+1))
		{
			printf("Felher in Threads1 \n");
			raise(SIGTERM);	
			exit(0);
		}
		printf("T1 write: %s\n", input);
		schedYield();
	}
}


/**
* Zweite Thread
*
* Liest aus RB12, schreibt in RB23
*/
int thread2(void* args)
{
	while(1)
	{ 
		
        	char output[400];
		size_t size = 400;
		int i ;
		// lies aus RB12
		if(!lies(RB12, 1, 2, output, &size))
		{
			printf("Felher in Threads2 \n");
			raise(SIGTERM);	
			exit(0);
		}
		printf("T2 read: %s\n", output);
		
		// Wandeln in grossbuchstaben
		for (i=0; i<strlen(output); i++)
    			output[i] = toupper(output[i]);
  		
		// schreibt Ergebniss in RB23
		if(!schreib(RB23, 2, 3, output, size))
		{
			printf("Felher in Threads1 \n");
			raise(SIGTERM);	
			exit(0);
		}
		printf("T2 write: %s\n", output);
		schedYield();
	}
}


/**
* Dritte Thread
*
* Zeichen, die im zuvor erhaltenen Telegramm an derselben 
* Stellen vorhanden war, auf leerzeichnen setzen
*/
int thread3(void* args)
{
	// Zuvor enthalten Telegramm
	char vor[400];
	//wird mit 0 initialisiert
	strcpy(vor, "\0");

	while(1)
	{ 
        	char output[400];
		size_t size = 400;
		// Liest aus RB23
		if(!lies(RB23, 2, 3, output, &size))
		{
			printf("Felher in Threads3 \n");
			raise(SIGTERM);	
			exit(0);
		}
		printf("T3 read: %s\n", output);
		
		int i = 0;
		//Copie des aktuelles Telegramm
		char out[strlen(output)];
		strcpy(out, output);

		//Zeichen, die in vorherige Telegramm war loeschen
		for (i=0; i<strlen(output) && i < strlen(vor); i++)
			if(output[i]==vor[i])
				output[i] = ' ';
  			
		//vorherige Telegramm ersetzen
		strcpy(vor, out); 
		
		if(!schreib(RB34, 3, 4, output, size))
		{
			printf("Felher in Threads3 \n");
			raise(SIGTERM);	
			exit(0);
		}
		printf("T3 write: %s\n", output);
		schedYield();
	}
}


/**
* Vierte Thread
*
* Liest aus RB34 und gibt das Ergebnis auf dem Bildchirm aus.
*/
int thread4(void* args)
{
	while(1)
	{ 
        	char output[400];
		size_t size = 400;
		// liest aus RB34
		if(!lies(RB34, 3, 4, output, &size))
			return;
	
		// Console Ausgabe
		printf("T4 read: %s\n", output);
	}
}


/**
* Signal Handler
*
* Ausschalten alle Threads und loeschen die Ringpuffer
*/
void signalhandler(int sig)
{
	//cheduler schaltet alle Prozesses aus
	chedClean();
	
	// Ringpuffer loeschen
	freeRb(RB12);
	freeRb(RB23);
	freeRb(RB34);

	printf("\n\tFinish\n");
	exit(0);
}
/*
\end{listingcont}
*/



